import {
  BleConnection
} from './inner_ble_connection.js';


function zip2UpdateTask(filePath) {
  const fs = wx.getFileSystemManager()
  return new Promise((resolve, reject) => {
    fs.unzip({
      zipFilePath: filePath,
      targetPath: wx.env.USER_DATA_PATH + `/yyy`,
      success: () => {
        fs.readdir({
          dirPath: wx.env.USER_DATA_PATH + `/yyy`,
          success: res => {
            resolve(res)
          }
        })
      },
      fail: (e) => {
        reject(e)
      }
    })
  })

}

/**
 * return
 * success [{"name","deviceId"}]
 * failed {"code"}
 */
function getConnectedDevices(deviceId) {
  return new Promise((resolve, reject) => {
    wx.getConnectedBluetoothDevices({
      services: ['2600'],
      success(res) {
        resolve(res)
      },
      fail(err) {
        console.error(err)
        resolve([])
      }
    })
  })
}

/**
 * 
 * @param {String} deviceId 
 * @param {Int8Array} cmd 
 */
function sendAndWaitResult(deviceId, cmd) {
  let UUID_BX_RECOVERY_OTA_SERVICE = "0000ee00-d102-e111-9b23-00025b00a5c7";
  let UUID_BX_RECOVERY_CTRL = "0000ee01-d102-e111-9b23-00025b00a5c7";
  return new Promise((resolve, reject) => {

    let timeoutId = setTimeout(function () {
      reject('timeout')
    }, 3000)

    let cb = function onResult(e) {
      if (e.deviceId == deviceId && e.serviceId.toLowerCase() == UUID_BX_RECOVERY_OTA_SERVICE && e.characteristicId.toLowerCase() == UUID_BX_RECOVERY_CTRL) {
        console.log('<-------' + buf2hex(e.value))
        let dv = new DataView(e.value)
        let rspCode = dv.getUint8(1)
        let requrstCode = dv.getUint8(0)
        if (rspCode == 0x80 && requrstCode == cmd[0]) {
          clearTimeout(timeoutId)
          wx.offBLECharacteristicValueChange(cb)
          resolve(dv)
        }
      }
    }
    let ab = new Uint8Array(cmd).buffer
    console.log('------->' + buf2hex(ab))
    wx.onBLECharacteristicValueChange(cb)
    wx.writeBLECharacteristicValue({
      characteristicId: UUID_BX_RECOVERY_CTRL,
      deviceId: deviceId,
      serviceId: UUID_BX_RECOVERY_OTA_SERVICE,
      writeType: 'write',
      value: ab,
      success(e) {
        // console.log(e)
      },
      fail(e) {
        wx.offBLECharacteristicValueChange(cb)
        clearTimeout(timeoutId)
        reject(e)
      }
    })
  })
}

function findDevice(deviceId) {
  return new Promise((resolve, reject) => {
    let timeoutId = setTimeout(function () {
      reject('timeout')
    }, 15000)

    let cb = function onDeviceFound(e) {
      console.log(e)
      e.devices.forEach(device => {
        if (device.deviceId == deviceId) {
          clearTimeout(timeoutId)
          wx.offBluetoothDeviceFound(cb)
          wx.stopBluetoothDevicesDiscovery()
          resolve(true)
        }
      });
    }
    wx.onBluetoothDeviceFound(cb)
    setTimeout(function () {
      wx.startBluetoothDevicesDiscovery({
        interval: 100,
        powerLevel: 'high',
        allowDuplicatesKey:true,
        success(e) {},
        fail(e) {
          wx.offBluetoothDeviceFound(cb)
          clearTimeout(timeoutId)
          reject(e)
        }
      })
    }, 2000)
  })
}

async function setup(bleConnectioin, deviceId) {
  let UUID_BX_RECOVERY_OTA_SERVICE = "0000ee00-d102-e111-9b23-00025b00a5c7";
  let UUID_BX_RECOVERY_CTRL = "0000ee01-d102-e111-9b23-00025b00a5c7";
  const devices = await getConnectedDevices(deviceId)
  if (devices && devices.length > 0) {
    //已连接，发现服务
    await bleConnectioin.setup(deviceId)
  } else {
    await bleConnectioin.connect(deviceId)
  }
  await bleConnectioin.enableNotification(UUID_BX_RECOVERY_OTA_SERVICE, UUID_BX_RECOVERY_CTRL, false)
}


async function execute(deviceId, otaTasks, exitActionCode, progressCallback) {

  let UUID_BX_RECOVERY_OTA_SERVICE = "0000ee00-d102-e111-9b23-00025b00a5c7";
  let UUID_BX_RECOVERY_CTRL = "0000ee01-d102-e111-9b23-00025b00a5c7";
  let UUID_BX_RECOVERY_DATA = "0000ee02-d102-e111-9b23-00025b00a5c7";
  const bleConnectioin = new BleConnection()

  await setup(bleConnectioin, deviceId)

  let checkRes1 = await sendAndWaitResult(deviceId, [2, 0])
  let state1 = checkRes1.getUint8(2)
  if (state1 != 0) {
    let inRes = await sendAndWaitResult(deviceId, [3, 0])
    if (inRes.getUint8(2) != 0) {
      throw "进入Recovery失败1"
    }
    bleConnectioin.disconnect()
    await findDevice(deviceId)

    await setup(bleConnectioin, deviceId)

    let checkRes2 = await sendAndWaitResult(deviceId, [2, 0])
    let state2 = checkRes2.getUint8(2)
    if (state2 != 0) {
      throw "进入Recovery失败2"
    }

  }

  let mtuSize = 23
  try {
    let mtuRes = await bleConnectioin.requestMtu(512)
    mtuSize = mtuRes.mtu
  } catch (ignore) {
    console.log(ignore)
  }
  if (mtuSize > 251) {
    mtuSize = 251
  }

  let splitSize = mtuSize - 3
  if (mtuSize >= 33) {
    splitSize = mtuSize - 13
  }

  /**
   * name
   * fileIndex: Int
   * rawData: ArrayBuffer
   * crc32: Int
   */
  for (let i = 0; i < otaTasks.length; i++) {
    console.log('---------------------------')
    let otaTask = otaTasks[i]
    console.log(otaTask)
    let rawData = otaTask.rawData
    let ab = new ArrayBuffer(11)
    var dv = new DataView(ab)
    dv.setInt8(0, 0)
    dv.setInt8(1, 0)
    dv.setInt8(2, otaTask.fileIndex)
    dv.setInt32(3, rawData.byteLength, true)
    dv.setInt32(7, otaTask.crc32, true)
    let array = Array.prototype.slice.call(new Uint8Array(ab))
    let startRsp = await sendAndWaitResult(deviceId, array)
    if (startRsp.getUint8(2) != 0) {
      throw 'start error'
    }

    let size = Math.floor(rawData.byteLength / splitSize)
    let remain = rawData.byteLength % splitSize

    console.log('size:' + size)
    console.log('remain:' + remain)

    for (let j = 0; j < size; j++) {
      let start = splitSize * j
      let end = splitSize * (j + 1)
      let contentBuffer = rawData.slice(start, end)
      // console.log(buf2hex(contentBuffer))
      await bleConnectioin.writeBuffer(UUID_BX_RECOVERY_OTA_SERVICE, UUID_BX_RECOVERY_DATA, false, contentBuffer)
    }
    if (remain > 0) {
      let start = splitSize * size
      let end = start + remain + 1
      let contentBuffer = rawData.slice(start, end)
      // console.log(buf2hex(contentBuffer))
      await bleConnectioin.writeBuffer(UUID_BX_RECOVERY_OTA_SERVICE, UUID_BX_RECOVERY_DATA, false, contentBuffer)
    }

    let endResult = await sendAndWaitResult(deviceId, [1, 0])

    if (endResult.getUint8(2) != 0) {
      throw "end Failed"
    }

  }
  sendAndWaitResult(deviceId, [4, 0, 0]).then()
}

function buf2hex(buffer) { // buffer is an ArrayBuffer
  return [...new Uint8Array(buffer)]
    .map(x => x.toString(16).padStart(2, '0'))
    .join(' ');
}

module.exports = {
  startRecovertOta: execute,
  zip2UpdateTask: zip2UpdateTask
}